package ppdz;

import java.util.LinkedList;
import rs.etf.pp1.symboltable.*;
import rs.etf.pp1.symboltable.concepts.*;
import rs.etf.pp1.symboltable.structure.*;
import rs.etf.pp1.symboltable.visitors.*;
import rs.etf.pp1.mj.runtime.*;

/**
 * Fixes jump addresses for if and while statements.
 *
 * @author Mirko Francuski
 */

public class JumpAddressFixer {

  private class AddressLevel {
    public LinkedList<Integer> addressToFix = new LinkedList<Integer>();
  }

  private AddressLevel currentAddressLevel = null;
  private AddressLevel currentLoopLevel = null;

  private LinkedList<Integer> loopTop = new LinkedList<Integer>();
  private LinkedList<AddressLevel> addressLevel = new LinkedList<AddressLevel>();
  private LinkedList<AddressLevel> loopLevel = new LinkedList<AddressLevel>();


  public void putJump(int adr) {
    Code.put(Code.jmp);
    Code.put2(adr - (Code.pc -1)); //+1 zbog jmp opCode
  }

  public void putFalseJmp(int op, int adr){
    Code.put(Code.jcc + Code.inverse[op]);
    Code.put2(adr);
  }

  public void fixup(int patchAdr, int blockEndAdr){
    Code.put2(patchAdr, blockEndAdr -(patchAdr+1)); ////+1 zbog jmp opCode
  }

  public void saveLoopTop(int adr){
//System.out.println("loopTop push:" + adr);
    loopTop.push(adr);
  }

  public int getLoopTop(){
//System.out.println("loopTop pop:" + loopTop.peek());
    return loopTop.poll();//pop
  }

  public boolean isInLoop(){
    return loopTop.isEmpty();
  }

  public void addBreak(int adr){
    currentLoopLevel.addressToFix.add(adr);
  }

  public void newAddressLevel(int adr, boolean inLoop){
    if(currentAddressLevel != null)
      addressLevel.push(currentAddressLevel);

    currentAddressLevel = new AddressLevel();

    currentAddressLevel.addressToFix.add(adr);

    if(inLoop){
      if(currentLoopLevel != null)
        loopLevel.push(currentLoopLevel);
      currentLoopLevel = currentAddressLevel;
    }
  }

  public void fixAddressLevel(int blockEndAdr, boolean inLoop){
    for(int patchAdr: currentAddressLevel.addressToFix)
      fixup(patchAdr,blockEndAdr);

    currentAddressLevel = addressLevel.poll();//pop

    if(inLoop){
      currentLoopLevel = loopLevel.poll();//pop
    }
  }

}